using System;
using System.Linq.Expressions;

namespace DuplexTransformers
{
    public class PropertyRelationship<TLeft, TRight, TLeftProp, TRightProp>
    {
        private readonly Expression<Func<TLeft, TLeftProp>> _leftFunc;
        private readonly ITransformer<TLeftProp, TRightProp> _transformer;
        private readonly Expression<Func<TRight, TRightProp>> _rightFunc;

        public PropertyRelationship(
            Expression<Func<TLeft, TLeftProp>> leftFunc,
            ITransformer<TLeftProp, TRightProp> transformer,
            Expression<Func<TRight, TRightProp>> rightFunc)
        {
            _leftFunc = leftFunc;
            _rightFunc = rightFunc;
            _transformer = transformer;
        }

        public void SetDestinationPropertyFromTransformedSourceProperty(TLeft left, TRight right)
        {
            SetDestinationPropertyFromTransformedSourceProperty(left, right, _leftFunc, _rightFunc, (l, r) => _transformer.Invoke(l, r));
        }

        public void SetDestinationPropertyFromTransformedSourceProperty(TRight right, TLeft left)
        {
            SetDestinationPropertyFromTransformedSourceProperty(right, left, _rightFunc, _leftFunc, (r, l) => _transformer.Invoke(r, l));
        }

        private static void SetDestinationPropertyFromTransformedSourceProperty<TSrc, TDest, TSrcProp, TDestProp>(
            TSrc src, 
            TDest dest, 
            Expression<Func<TSrc, TSrcProp>> srcExpr, 
            Expression<Func<TDest, TDestProp>> destExpr,
            Func<TSrcProp, TDestProp, TDestProp> func)
        {
            var srcValue = srcExpr.Compile()(src);
            try {
                var destValue = default(TDestProp);
                if (dest != null)
                {
                    destValue = destExpr.Compile()(dest);
                    
                }
                
                
            var mediatedValue = func(srcValue, destValue);
            DynamicMutator.InvokeMutator(destExpr, dest, mediatedValue);
            }
            catch (Exception ex)
            {

                throw;
            }
        }
    }
}